package cn.blu10ph.trustshare.util;

import cn.blu10ph.trustshare.bean.msg.TrustMsg;
import cn.blu10ph.trustshare.bean.node.TrustNode;
import cn.blu10ph.trustshare.bean.notice.Notice;
import cn.blu10ph.trustshare.bean.notice.TrustNotice;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 * <p> ConvertUtil </p >
 *
 * @author cxx
 * @date 2024/4/6 22:30
 */
public class HashAndSignUtil {

    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    private static final SecureRandom RANDOM = new SecureRandom();
    private static final int RANDOM_SIZE = 5;

    public static String getNodeHash(TrustNode node) throws NoSuchAlgorithmException {
        StringBuilder signStr = new StringBuilder()
                .append(node.getNodeId())
                .append(node.getHost())
                .append(node.getPort());
        if(!ObjectUtils.isEmpty(node.getBase())){
            signStr.append(node.getBase().getNodeId())
                    .append(node.getBase().getHost())
                    .append(node.getBase().getPort());
        }
        signStr.append(node.getTime())
                .append(node.getNonce());

        return HashAndSignUtil.shaHash(signStr.toString(), node.getNonce());
    }

    public static boolean checkNodeSign(TrustNode node, PGPPublicKey publicKey) {
        try {
            String msgHash = HashAndSignUtil.getNodeHash(node);
            return PgpUtil.verifySignature(msgHash, node.getSign(), publicKey);
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public static String getMsgHash(TrustMsg msg) throws NoSuchAlgorithmException {
        String params = msg.getParams();
        StringBuilder signStr = new StringBuilder()
                .append(msg.getType())
                .append(msg.getDirection())
                .append(msg.getFrom())
                .append(msg.getTo());
        if(StringUtils.hasText(params)){
            String paramsHash = null;
            try {
                paramsHash = HashAndSignUtil.shaHash(params, null);
            } catch (NoSuchAlgorithmException ex) {
                ex.printStackTrace();
            }
            signStr.append(paramsHash);
        }
        signStr.append(msg.getTime())
                .append(msg.getNonce());

        return HashAndSignUtil.shaHash(signStr.toString(), msg.getNonce());
    }

    public static boolean checkMsgSign(TrustMsg msg, PGPPublicKey publicKey) {
        try {
            String msgHash = HashAndSignUtil.getMsgHash(msg);
            return PgpUtil.verifySignature(msgHash, msg.getSign(), publicKey);
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public static String getNoticeHash(Notice notice) throws NoSuchAlgorithmException {
        StringBuilder infoStr = new StringBuilder()
                .append(notice.getFromNode())
                .append(notice.getBaseHash())
                .append(notice.getType())
                .append(notice.getContent())
                .append(notice.getTime());

        return HashAndSignUtil.shaHash(infoStr.toString(), String.valueOf(notice.getTime()));
    }

    public static boolean checkNoticeHash(Notice notice) {
        try {
            String msgHash = HashAndSignUtil.getNoticeHash(notice);
            return msgHash.equals(notice.getInfoHash());
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public static String getNoticeSignHash(TrustNotice notice) throws NoSuchAlgorithmException {
        StringBuilder signStr = new StringBuilder()
                .append(notice.getInfoHash())
                .append(notice.getTime())
                .append(notice.getNonce());

        return HashAndSignUtil.shaHash(signStr.toString(), notice.getNonce());
    }

    public static boolean checkNoticeSign(TrustNotice notice, PGPPublicKey publicKey) {
        try {
            String msgHash = HashAndSignUtil.getNoticeSignHash(notice);
            return PgpUtil.verifySignature(msgHash, notice.getSign(), publicKey);
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public static String shaHash(String content, String salt) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        //MessageDigest md = MessageDigest.getInstance("SHA-256");
        //MessageDigest md = MessageDigest.getInstance("SHA-512");
        if(StringUtils.hasText(salt)) {
            md.update(salt.getBytes(StandardCharsets.UTF_8));
        }
        byte[] bytes = md.digest(content.getBytes(StandardCharsets.UTF_8));
        return ConvertUtil.hex2String(bytes);
    }

    public static String getNonce(Long time) {
        SecureRandom rng = new SecureRandom(time.toString().getBytes(StandardCharsets.UTF_8));

        StringBuilder sb = new StringBuilder(RANDOM_SIZE);
        for (int i = 0; i < RANDOM_SIZE; i++) {
            int index = rng.nextInt(CHARACTERS.length());
            sb.append(CHARACTERS.charAt(index));
        }

        return sb.toString();
    }

}
